package co.edu.usbcali.ai.ga;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class Generation implements Serializable{
	private int number;
	private int size;
	private List<Individual> list;
	private float average=0;
	private float total=0;
	private float max=0;
	private float min=0;
	
	public String toString(){
		String ret="Generación:"+number+"\n";
		for (Individual individual : list) {
			ret+=" "+individual.toString();
		}
		ret+="\n Promedio="+average;
		ret+=" Total="+total;
		ret+=" Min="+min;
		ret+=" Max="+max;
		return ret;
	}
	
	/* **********************************************
	 * CONSTRUCTORS
	 * **********************************************/

	public Generation(int size){
		list=new ArrayList<Individual>(size);
		this.size=size;
	}

	/* **********************************************
	 * 
	 * **********************************************/

	public static List<ICromosom> getCromosomsList(List<Individual> p_list){
		List<ICromosom> cromosomsList=new ArrayList<ICromosom>();
		Iterator<Individual> iterator=p_list.iterator();
		while (iterator.hasNext()){
			cromosomsList.add(iterator.next().getCromosom());
		}
		return cromosomsList;
	}
	
	public static List<Object> getFenotypesList(List<Individual> p_list,Breed breed){
		List<Object> fenotypesList=new ArrayList<Object>();
		Iterator<Individual> iterator=p_list.iterator();
		for (Individual individual : p_list) {
			fenotypesList.add(breed.getFenotype(individual));
		}
		return fenotypesList;
	}
	
	public static List<Individual> createIndividualsList(List<ICromosom> cromosomsList,int p_generation,Ga ga){
		List<Individual> childrenList=new ArrayList<Individual>();
		Iterator<ICromosom> iterator=cromosomsList.iterator();
		while (iterator.hasNext()){
			Individual individual=new Individual(ga.getNextPrefix(),iterator.next());
			individual.setBirthGeneration(p_generation);
			childrenList.add(individual);
		}
		return childrenList;
	}


	public float evaluate(Breed breed){
		float eval;
		total=0;
		boolean first=true;
		List<EvaluationThread> evalThreads=new ArrayList<EvaluationThread>();
		for (Individual individual : list) {
			EvaluationThread et=new EvaluationThread(individual,breed);
			evalThreads.add(et);
//			eval=race.evaluate(individual);
			et.start();
		}
		for (EvaluationThread thread: evalThreads){
			try {
				thread.join();
				eval=thread.getEval();
				if (first){
					first=false;
					min=eval;
					max=eval;
				}else{
					if (eval<min){
						min=eval;
					}
					if (eval>max){
						max=eval;
					}				
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		average=total/size;
		return total;
	}

	public List<Individual> successfulIndividuals(float p_limit){
		List<Individual> successfulList=new ArrayList<Individual>();
		Iterator<Individual> iterator=getList().iterator();
		while (iterator.hasNext()){
			Individual individual=iterator.next();
			if (individual.getEvaluation()>=p_limit){
				successfulList.add(individual);
			}
		}
		return successfulList;
	}
	
	public Individual maxIndividual(){
		List<Individual> orderedList=new ArrayList<Individual>(list);
		Collections.sort(orderedList);
		return orderedList.get(size-1);
	}

	
	/* **********************************************
	 * GETTERS AND SETTERS
	 * **********************************************/

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public List<Individual> getList() {
		return list;
	}

	public void setList(List<Individual> list) {
		this.list = list;
	}

	public float getAverage() {
		return average;
	}

	public void setAverage(float average) {
		this.average = average;
	}

	public float getTotal() {
		return total;
	}

	public void setTotal(float total) {
		this.total = total;
	}

	public float getMax() {
		return max;
	}

	public void setMax(float max) {
		this.max = max;
	}

	public float getMin() {
		return min;
	}

	public void setMin(float min) {
		this.min = min;
	}

	public void serialize(){
		FileOutputStream fos;
		try {
			fos = new FileOutputStream("Generation"+this.number);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(this);
			oos.flush();
			oos.close(); 
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static Generation unSerialize(String name){
		FileInputStream fis;
		try {
			fis = new FileInputStream(name);
			ObjectInputStream ois = new ObjectInputStream(fis);
			Generation object2 = (Generation)ois.readObject();
			ois.close(); 
			return object2;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}
